package cn.mujiankeji.mbrowser.工具.网络

import cn.mujiankeji.mbrowser.工具.mlog
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.plugins.*
import io.ktor.client.request.*
import io.ktor.client.request.forms.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.util.toMap
import io.ktor.utils.io.*
import io.ktor.utils.io.core.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import java.io.File
import java.net.URLEncoder
import java.nio.charset.Charset

/**
 * 网络操作工具类
 * 基于 Ktor 实现的网络请求工具，提供常用的 GET、POST、下载等方法
 */
object 网络操作 : KoinComponent {

    // 通过 Koin 注入 HttpClient
    private val httpClient: HttpClient by inject()

    /**
     * GET 请求，返回字符串
     * @param url 请求地址
     * @param ua 用户代理
     * @param headers 请求头
     * @param outTimer 请求超时时间（毫秒）
     * @return 响应文本，失败返回 null
     */
    suspend fun 获取文本(
        url: String,
        ua: String? = null,
        headers: Map<String, String>? = null,
        outTimer: Long? = null,
        onError : ((String,Exception) -> Unit) ?= null,

        ): String? {
        return try {
            val response = httpClient.get(url) {
                // 设置默认接受类型
                header(HttpHeaders.Accept, "text/html, application/xhtml+xml, application/xml; q=0.9, */*; q=0.8")
                header(HttpHeaders.AcceptLanguage, "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3")
                header(HttpHeaders.AcceptEncoding, "gzip, deflate")
                header(HttpHeaders.Connection, "keep-alive")
                header(HttpHeaders.CacheControl, "no-cache")
                header(HttpHeaders.Pragma, "no-cache")

                // 设置用户代理
                ua?.let { header(HttpHeaders.UserAgent, it) }
                
                // 设置自定义请求头
                headers?.forEach { (k, v) -> header(k, v) }
                
                // 设置超时
                outTimer?.let {
                    timeout {
                        requestTimeoutMillis = it
                        connectTimeoutMillis = it
                        socketTimeoutMillis = it
                    }
                }
            }


            if (response.status.value == 200) {
                val bytes = response.body<ByteArray>()
                // 1. 从响应头获取 charset
                val contentType = response.headers["Content-Type"]
                var charset: String? = null
                contentType?.let {
                    val match = Regex("charset=([\\w-]+)").find(it)
                    charset = match?.groupValues?.get(1)
                }
                // 2. 如果没有，从内容 meta 标签获取
                if (charset == null) {
                    val snippet = String(bytes, 0, minOf(bytes.size, 2048), Charsets.ISO_8859_1)
                    val metaMatch = Regex("""<meta[^>]+charset=["']?([\w-]+)["']?""", RegexOption.IGNORE_CASE).find(snippet)
                    charset = metaMatch?.groupValues?.get(1)
                }
                // 3. 默认 UTF-8
                if (charset == null) charset = "UTF-8"
                // 4. 解码
                return String(bytes, Charset.forName(charset))
            } else {
                mlog("url",url,ua)
                onError?.invoke(response.status.value.toString() + "," + response.status.description,
                    Exception(response.status.description))
                null
            }
        } catch (e: Exception) {
            e.printStackTrace()
            onError?.invoke(e.toString(),e)
            null
        }
    }

    /**
     * GET 请求，返回字节数组
     * @param url 请求地址
     * @param ua 用户代理
     * @param headers 请求头
     * @param 超时 请求超时时间（毫秒）
     * @return 响应字节数组，失败返回 null
     */
    suspend fun 获取字节(
        url: String,
        ua: String? = null,
        headers: Map<String, String>? = null,
        超时: Long? = null
    ): ByteArray? {
        return try {
            val response = httpClient.get(url) {
                // 设置默认接受类型
                header(HttpHeaders.Accept, "text/html, application/xhtml+xml, application/xml; q=0.9, */*; q=0.8")
                
                // 设置用户代理
                ua?.let { header(HttpHeaders.UserAgent, it) }
                
                // 设置自定义请求头
                headers?.forEach { (k, v) -> header(k, v) }
                
                // 设置超时
                超时?.let { 
                    timeout {
                        requestTimeoutMillis = it
                        connectTimeoutMillis = it
                        socketTimeoutMillis = it
                    }
                }
            }
            response.body<ByteArray>()
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }

    /**
     * POST 表单请求
     * @param url 请求地址
     * @param 表单数据 表单数据
     * @param ua 用户代理
     * @param headers 请求头
     * @param 编码 URL编码字符集
     * @param 超时 请求超时时间（毫秒）
     * @return 响应文本，失败返回 null
     */
    suspend fun 提交表单(
        url: String,
        表单数据: Map<String, String>,
        ua: String? = null,
        headers: Map<String, String>? = null,
        编码: String? = null,
        超时: Long? = null
    ): String? {
        return try {
            val response = httpClient.submitForm(
                url = url,
                formParameters = Parameters.build {
                    表单数据.forEach { (k, v) -> 
                        if (编码 != null) {
                            append(k, URLEncoder.encode(v, 编码))
                        } else {
                            append(k, v)
                        }
                    }
                }
            ) {
                // 设置用户代理
                ua?.let { header(HttpHeaders.UserAgent, it) }
                
                // 设置自定义请求头
                headers?.forEach { (k, v) -> 
                    if (编码 != null) {
                        header(k, URLEncoder.encode(v, 编码))
                    } else {
                        header(k, v)
                    }
                }
                
                // 设置超时
                超时?.let { 
                    timeout {
                        requestTimeoutMillis = it
                        connectTimeoutMillis = it
                        socketTimeoutMillis = it
                    }
                }
            }
            response.bodyAsText()
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }

    /**
     * POST JSON 请求
     * @param url 请求地址
     * @param json JSON 字符串
     * @param ua 用户代理
     * @param headers 请求头
     * @param 超时 请求超时时间（毫秒）
     * @return 响应文本，失败返回 null
     */
    suspend fun 提交JSON(
        url: String,
        json: String,
        ua: String? = null,
        headers: Map<String, String>? = null,
        超时: Long? = null
    ): String? {
        return try {
            val response = httpClient.post(url) {
                // 设置内容类型为 JSON
                contentType(ContentType.Application.Json)
                
                // 设置请求体
                setBody(json)
                
                // 设置用户代理
                ua?.let { header(HttpHeaders.UserAgent, it) }
                
                // 设置自定义请求头
                headers?.forEach { (k, v) -> header(k, v) }
                
                // 设置超时
                超时?.let { 
                    timeout {
                        requestTimeoutMillis = it
                        connectTimeoutMillis = it
                        socketTimeoutMillis = it
                    }
                }
            }
            response.bodyAsText()
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }

    /**
     * 下载文件
     * @param url 下载地址
     * @param 保存路径 文件保存路径
     * @param ua 用户代理
     * @param headers 请求头
     * @param 进度回调 下载进度回调，参数为已下载字节数和总字节数
     * @param 超时 请求超时时间（毫秒）
     * @return 是否下载成功
     */
    suspend fun 下载文件(
        url: String,
        保存路径: String,
        ua: String? = null,
        headers: Map<String, String>? = null,
        进度回调: ((已下载: Long, 总大小: Long) -> Unit)? = null,
        超时: Long? = null
    ): Boolean {
        return try {
            val response = httpClient.get(url) {
                // 设置用户代理
                ua?.let { header(HttpHeaders.UserAgent, it) }
                
                // 设置自定义请求头
                headers?.forEach { (k, v) -> header(k, v) }
                
                // 设置超时（下载通常需要更长时间）
                超时?.let { 
                    timeout {
                        requestTimeoutMillis = it
                        connectTimeoutMillis = it
                        socketTimeoutMillis = it
                    }
                }
            }
            
            // 获取文件总大小
            val contentLength = response.contentLength() ?: -1L
            
            // 创建文件并写入数据
            val file = File(保存路径)
            
            // 确保父目录存在
            file.parentFile?.mkdirs()
            
            withContext(Dispatchers.IO) {
                file.outputStream().use { output ->
                    val channel = response.bodyAsChannel()
                    var 已下载 = 0L
                    
                    val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
                    var bytesRead: Int
                    
                    while (true) {
                        bytesRead = channel.readAvailable(buffer, 0, buffer.size)
                        if (bytesRead < 0) break
                        
                        output.write(buffer, 0, bytesRead)
                        已下载 += bytesRead
                        
                        // 回调进度
                        进度回调?.invoke(已下载, contentLength)
                    }
                }
            }
            
            true
        } catch (e: Exception) {
            e.printStackTrace()
            false
        }
    }

    /**
     * 获取重定向地址
     * @param url 原始地址
     * @param ua 用户代理
     * @param headers 请求头
     * @param 超时 请求超时时间（毫秒）
     * @return 重定向后的地址，如果没有重定向则返回原地址
     */
    suspend fun 获取重定向地址(
        url: String,
        ua: String? = null,
        headers: Map<String, String>? = null,
        超时: Long? = null
    ): String {
        return try {
            val response = httpClient.get(url) {
                // 设置用户代理
                ua?.let { header(HttpHeaders.UserAgent, it) }
                
                // 设置自定义请求头
                headers?.forEach { (k, v) -> header(k, v) }
                
                // 设置超时
                超时?.let { 
                    timeout {
                        requestTimeoutMillis = it
                        connectTimeoutMillis = it
                        socketTimeoutMillis = it
                    }
                }
            }
            
            // 返回最终 URL
            response.request.url.toString()
        } catch (e: Exception) {
            e.printStackTrace()
            url // 发生错误时返回原始 URL
        }
    }

    /**
     * 使用指定字符集解码响应
     * @param url 请求地址
     * @param 字符集 解码字符集
     * @param ua 用户代理
     * @param headers 请求头
     * @param 超时 请求超时时间（毫秒）
     * @return 解码后的文本，失败返回 null
     */
    suspend fun 获取指定编码文本(
        url: String,
        字符集: String,
        ua: String? = null,
        headers: Map<String, String>? = null,
        超时: Long? = null
    ): String? {
        return try {
            val response = httpClient.get(url) {
                // 设置默认接受类型
                header(HttpHeaders.Accept, "text/html, application/xhtml+xml, application/xml; q=0.9, */*; q=0.8")
                
                // 设置用户代理
                ua?.let { header(HttpHeaders.UserAgent, it) }
                
                // 设置自定义请求头
                headers?.forEach { (k, v) -> header(k, v) }
                
                // 设置超时
                超时?.let { 
                    timeout {
                        requestTimeoutMillis = it
                        connectTimeoutMillis = it
                        socketTimeoutMillis = it
                    }
                }
            }
            
            // 使用指定字符集解码
            String(response.body<ByteArray>(), Charset.forName(字符集))
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }
    
    /**
     * 发起 HEAD 请求获取资源信息
     * @param url 请求地址
     * @param ua 用户代理
     * @param headers 请求头
     * @param 超时 请求超时时间（毫秒）
     * @return 响应头信息，失败返回空 Map
     */
    suspend fun 获取资源信息(
        url: String,
        ua: String? = null,
        headers: Map<String, String>? = null,
        超时: Long? = null
    ): Map<String, List<String>> {
        return try {
            val response = httpClient.head(url) {
                // 设置用户代理
                ua?.let { header(HttpHeaders.UserAgent, it) }
                
                // 设置自定义请求头
                headers?.forEach { (k, v) -> header(k, v) }
                
                // 设置超时
                超时?.let { 
                    timeout {
                        requestTimeoutMillis = it
                        connectTimeoutMillis = it
                        socketTimeoutMillis = it
                    }
                }
            }
            
            // 返回响应头
            response.headers.toMap()
        } catch (e: Exception) {
            e.printStackTrace()
            emptyMap()
        }
    }
}